package pl.edu.pw.mini.java2.Communicator.Client;

import java.net.*;
import java.io.*;

/**
 * 
 * @author Dymitr Załunin, Kamil Łysik.
 * Główna klasa aplikacji klienckiej.
 * Odpowiada za nawiązawywanie i utrzywymanie połączenia z serwerem.
 */
public class Client {
	/**
	 * TODO : dodac dokumentację.
	 */

	private DataOutputStream outToServer = null;
	private DataInputStream inFromServer = null;
	private Socket server = null;

	private WriterWorker writer;
	private ReaderWorker reader;

	private LoginInfo info;

	private ChatListener chatListener = null;
	private ClientListener clientsListener = null;

	/**
	 * Zwraca referencję do obiektu ClientListener.
	 * @return ClientListener.
	 */
	public ClientListener getClientListener() {
		return clientsListener;
	}

	/**
	 * Dodaję ineterfejs {@link ClientListener} .
	 * @param contactsListener Interfejs który jest implementowany przez okno główne aplikacji klienckiej.
	 */
	public void addClientListener(ClientListener contactsListener) {
		this.clientsListener = contactsListener;
		writer.sendMessage(new Message(Message.GET_AVAILABLE_CONTACT, ""
				.getBytes()));
	}

	/**
	 * Zwraca referencję do obiektu ChatListener.
	 * @return Interfejs który jest implementowany przez okno główne aplikacji klienckiej.
	 */
	public ChatListener getChatListener() {
		return chatListener;
	}
	
	/**
	 * Dodaję ineterfejs {@link ChatListener} .
	 * @param listener Interfejs który jest implementowany przez okno główne aplikacji klienckiej.
	 */
	public void addChatListener(ChatListener listener) {
		chatListener = listener;
	}	

	/**
	 * Zwraca informacje o kliencie.
	 * @return LoginInfo.
	 */
	public LoginInfo getLoginInfo() {
		return info;
	}

	/**
	 * Dodaje infomacje o kliencie.
	 * @param info Obiekt z informacja o kliencie.
	 */
	public void setLoginInfo(LoginInfo info) {
		this.info = info;
	}

	/**
	 * Konstruktor bezparametrowy, tworzący nową instancję klienta.
	 */
	public Client() {
	}

	/**
	 * Konstruktor tworzący nową instancję klienta.
	 * @param login
	 */
	public Client(String login) {
		setLoginInfo(new LoginInfo(login));
	}

	/**
	 * Metoda, który łączy sie z klientem.
	 * @param addr Adres IP serwera aplikacji.
	 * @param port Port na którym nasłuchuje serwer aplikacji.
	 * @throws Exception Wyjatek jest rzucany gdy podano błędny adres serwera albo serwer jest niedostepny.
	 */
	public void ConnectToServer(String addr, int port) throws Exception {

		try {
			server = new Socket(addr, port);
			outToServer = new DataOutputStream(server.getOutputStream());
			inFromServer = new DataInputStream(server.getInputStream());
		} catch (Exception ex) {
			//out.println("Can not connect with server!");
			throw ex;
		}
	}

	/**
	 * Metoda wykonująca zapytanie do serwera z prosbą o zalogowanie klienta. 
	 * @throws Exception Wyjątek jest rzucany gdy logowanie sie nie powiedzie.
	 */
	public void LogOnRequest() throws Exception {
		reader.setLastState(-1);
		if (info.getLogin().length() == 0)
			throw new Exception("Login failed");
		writer.sendMessage(new Message(Message.LOGIN_REQUEST, info.getLogin()
				.getBytes()));
		int state = -1;
		while ((state = reader.getLastState()) == -1)
			Thread.sleep(10);
		if (state == Message.ERROR)
			throw new Exception("Logon failed");
	}

	/**
	 * 
	 * @param name Login klienta, którego dany obiekt typu Client zaprasza do czatu.  
	 * @throws Exception Wyjątek jest rzucany gdy klient o loginie name jest zajety albo niepodłaczony.
	 */
	public void sendChatRequest(String name) throws Exception {
		reader.setLastState(-1);
		writer.sendMessage(new Message(Message.CHAT_REQUEST, name.getBytes()));
		int state = -1;
		while (state == -1) {
			state = reader.getLastState();
			Thread.sleep(10);
		}
		if (state == Message.ERROR)
			throw new Exception("Cannot get request");

	}

	/**
	 * Wysyła zapytanie do serwera z prośbą o przerwanie czatu.
	 */
	public void sendChatRejectRequest() {
		writer.sendMessage(new Message(Message.CHAT_REJECT, "".getBytes()));
	}

	/**
	 * Wysyła wiadomośc tekstową do serwera, która później jest wysyłana do odbiorcy(klienta z którym jest prowadzony czat).
	 * @param msg Treść wiadomości.
	 */
	public void sendChatTextMessage(String msg) {
		writer.sendMessage(new Message(Message.TEXT_MESSAGE, msg.getBytes()));		
	}

	/**
	 * Wysyła próbkę dzwięku do serwera, która później jest wysyłana do odbiorcy(klienta z którym jest prowadzona rozmowa).
	 * @param msg
	 */
	public void sendChatVoiceMessage(byte[] msg) {
		writer.sendMessage(new Message(Message.VOICE_MESSAGE, msg));
	}

	/**
	 * Metoda  uruchamiająca komunikacje z serwerem.
	 */
	public void start() {
		writer = new WriterWorker(outToServer);
		reader = new ReaderWorker(inFromServer, this);
		writer.start();
		reader.start();
	}

	/**
	 * Metoda zamykająca komunikację z serwerem.
	 */
	public void close() {
		System.out.println("close() called");
		writer.close();
		reader.close();
		try {
			server.close();
		} catch (IOException e) {
		}
		writer = null;
		reader = null;
	}
}